/*
 * Copyright 2015-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.maven;

import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Produces ONOS component configuration catalogue resources.
 */
@Mojo(name = "cfg", defaultPhase = LifecyclePhase.GENERATE_RESOURCES)
@java.lang.SuppressWarnings("squid:S1148")
public class OnosCfgMojo extends AbstractMojo {

    private static final String COMPONENT = "org.apache.felix.scr.annotations.Component";
    private static final String PROPERTY = "org.apache.felix.scr.annotations.Property";
    private static final String SEP = "|";

    /**
     * The directory where the generated catalogue file will be put.
     */
    @Parameter(defaultValue = "${basedir}")
    protected File srcDirectory;

    /**
     * The directory where the generated catalogue file will be put.
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}")
    protected File dstDirectory;

    @Override
    public void execute() throws MojoExecutionException {
        getLog().info("Generating ONOS component configuration catalogues...");
        try {
            JavaProjectBuilder builder = new JavaProjectBuilder();
            builder.addSourceTree(new File(srcDirectory, "src/main/java"));
            builder.getClasses().forEach(this::processClass);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    private void processClass(JavaClass javaClass) {
        boolean isComponent = javaClass.getAnnotations().stream()
                .map(ja -> ja.getType().getName().equals(COMPONENT))
                .findFirst().isPresent();
        if (isComponent) {
            List<String> lines = new ArrayList<>();
            javaClass.getFields().forEach(field -> processField(lines, javaClass, field));
            if (!lines.isEmpty()) {
                writeCatalog(javaClass, lines);
            }
        }
    }

    private void writeCatalog(JavaClass javaClass, List<String> lines) {
        File dir = new File(dstDirectory, javaClass.getPackageName().replace('.', '/'));
        dir.mkdirs();

        File cfgDef = new File(dir, javaClass.getName().replace('.', '/') + ".cfgdef");
        try (FileWriter fw = new FileWriter(cfgDef);
             PrintWriter pw = new PrintWriter(fw)) {
            pw.println("# This file is auto-generated by onos-maven-plugin");
            lines.forEach(pw::println);
        } catch (IOException e) {
            System.err.println("Unable to write catalog for " + javaClass.getName());
            e.printStackTrace();
        }
    }

    private void processField(List<String> lines, JavaClass javaClass, JavaField field) {
        field.getAnnotations().forEach(ja -> {
            if (ja.getType().getName().equals(PROPERTY)) {
                lines.add(expand(javaClass, ja.getNamedParameter("name").toString()) +
                                  SEP + type(field) +
                                  SEP + defaultValue(javaClass, field, ja) +
                                  SEP + description(ja));
            }
        });
    }

    // TODO: Stuff below is very much hack-ish and should be redone; it works for now though.

    private String description(JavaAnnotation annotation) {
        String description = (String) annotation.getNamedParameter("label");
        return description.replaceAll("\" \\+ \"", "")
                .replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
    }

    private String type(JavaField field) {
        String ft = field.getType().getName().toUpperCase();
        return ft.equals("INT") ? "INTEGER" : ft;
    }

    private String defaultValue(JavaClass javaClass, JavaField field,
                                JavaAnnotation annotation) {
        String ft = field.getType().getName().toLowerCase();
        String defValueName = ft.equals("boolean") ? "boolValue" :
                ft.equals("string") ? "value" : ft + "Value";
        Object dv = annotation.getNamedParameter(defValueName);
        return dv == null ? "" : expand(javaClass, dv.toString());
    }

    private String stripQuotes(String string) {
        return string.trim().replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
    }

    private String expand(JavaClass javaClass, String value) {
        JavaField field = javaClass.getFieldByName(value);
        return field == null ? stripQuotes(value) :
                stripQuotes(field.getCodeBlock().replaceFirst(".*=", "").replaceFirst(";$", ""));
    }

}
